/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package a_vcard.android.telephony;

//import android.content.Context;
//import android.content.Intent;
//import android.database.Cursor;
//import android.net.Uri;
//import android.os.SystemProperties;
import a_vcard.android.provider.Contacts;
import a_vcard.android.text.Editable;
import a_vcard.android.text.SpannableStringBuilder;
import a_vcard.android.text.TextUtils;
//import android.util.SparseIntArray;

import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Various utilities for dealing with phone number strings.
 */
public class PhoneNumberUtils
{
//    /*
//     * Special characters
//     *
//     * (See "What is a phone number?" doc)
//     * 'p' --- GSM pause character, same as comma
//     * 'n' --- GSM wild character
//     * 'w' --- GSM wait character
//     */
//    public static final char PAUSE = ',';
//    public static final char WAIT = ';';
//    public static final char WILD = 'N';
//
//    /*
//     * TOA = TON + NPI
//     * See TS 24.008 section 10.5.4.7 for details.
//     * These are the only really useful TOA values
//     */
//    public static final int TOA_International = 0x91;
//    public static final int TOA_Unknown = 0x81;
//
//    /*
//     * global-phone-number = ["+"] 1*( DIGIT / written-sep )
//     * written-sep         = ("-"/".")
//     */
//    private static final Pattern GLOBAL_PHONE_NUMBER_PATTERN =
//            Pattern.compile("[\\+]?[0-9.-]+");
//
//    /** True if c is ISO-LATIN characters 0-9 */
//    public static boolean
//    isISODigit (char c) {
//        return c >= '0' && c <= '9';
//    }
//
//    /** True if c is ISO-LATIN characters 0-9, *, # */
//    public final static boolean
//    is12Key(char c) {
//        return (c >= '0' && c <= '9') || c == '*' || c == '#';
//    }
//
//    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD  */
//    public final static boolean
//    isDialable(char c) {
//        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
//    }
//
//    /** True if c is ISO-LATIN characters 0-9, *, # , + (no WILD)  */
//    public final static boolean
//    isReallyDialable(char c) {
//        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
//    }
//
//    /** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE   */
//    public final static boolean
//    isNonSeparator(char c) {
//        return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
//                || c == WILD || c == WAIT || c == PAUSE;
//    }
//
//    /** This any anything to the right of this char is part of the
//     *  post-dial string (eg this is PAUSE or WAIT)
//     */
//    public final static boolean
//    isStartsPostDial (char c) {
//        return c == PAUSE || c == WAIT;
//    }
//
//    /** Extracts the phone number from an Intent.
//     *
//     * @param intent the intent to get the number of
//     * @param context a context to use for database access
//     *
//     * @return the phone number that would be called by the intent, or
//     *         <code>null</code> if the number cannot be found.
//     */
//    public static String getNumberFromIntent(Intent intent, Context context) {
//        String number = null;
//
//        Uri uri = intent.getData();
//        String scheme = uri.getScheme();
//
//        if (scheme.equals("tel")) {
//            return uri.getSchemeSpecificPart();
//        }
//
//        if (scheme.equals("voicemail")) {
//            return TelephonyManager.getDefault().getVoiceMailNumber();
//        }
//
//        if (context == null) {
//            return null;
//        }
//
//        String type = intent.resolveType(context);
//
//        Cursor c = context.getContentResolver().query(
//                uri, new String[]{ Contacts.People.Phones.NUMBER },
//                null, null, null);
//        if (c != null) {
//            try {
//                if (c.moveToFirst()) {
//                    number = c.getString(
//                            c.getColumnIndex(Contacts.People.Phones.NUMBER));
//                }
//            } finally {
//                c.close();
//            }
//        }
//
//        return number;
//    }
//
//    /** Extracts the network address portion and canonicalizes
//     *  (filters out separators.)
//     *  Network address portion is everything up to DTMF control digit
//     *  separators (pause or wait), but without non-dialable characters.
//     *
//     *  Please note that the GSM wild character is allowed in the result.
//     *  This must be resolved before dialing.
//     *
//     *  Allows + only in the first  position in the result string.
//     *
//     *  Returns null if phoneNumber == null
//     */
//    public static String
//    extractNetworkPortion(String phoneNumber) {
//        if (phoneNumber == null) {
//            return null;
//        }
//
//        int len = phoneNumber.length();
//        StringBuilder ret = new StringBuilder(len);
//        boolean firstCharAdded = false;
//
//        for (int i = 0; i < len; i++) {
//            char c = phoneNumber.charAt(i);
//            if (isDialable(c) && (c != '+' || !firstCharAdded)) {
//                firstCharAdded = true;
//                ret.append(c);
//            } else if (isStartsPostDial (c)) {
//                break;
//            }
//        }
//
//        return ret.toString();
//    }
//
//    /**
//     * Strips separators from a phone number string.
//     * @param phoneNumber phone number to strip.
//     * @return phone string stripped of separators.
//     */
//    public static String stripSeparators(String phoneNumber) {
//        if (phoneNumber == null) {
//            return null;
//        }
//        int len = phoneNumber.length();
//        StringBuilder ret = new StringBuilder(len);
//
//        for (int i = 0; i < len; i++) {
//            char c = phoneNumber.charAt(i);
//            if (isNonSeparator(c)) {
//                ret.append(c);
//            }
//        }
//
//        return ret.toString();
//    }
//
//    /** or -1 if both are negative */
//    static private int
//    minPositive (int a, int b) {
//        if (a >= 0 && b >= 0) {
//            return (a < b) ? a : b;
//        } else if (a >= 0) { /* && b < 0 */
//            return a;
//        } else if (b >= 0) { /* && a < 0 */
//            return b;
//        } else { /* a < 0 && b < 0 */
//            return -1;
//        }
//    }
//
//    /** index of the last character of the network portion
//     *  (eg anything after is a post-dial string)
//     */
//    static private int
//    indexOfLastNetworkChar(String a) {
//        int pIndex, wIndex;
//        int origLength;
//        int trimIndex;
//
//        origLength = a.length();
//
//        pIndex = a.indexOf(PAUSE);
//        wIndex = a.indexOf(WAIT);
//
//        trimIndex = minPositive(pIndex, wIndex);
//
//        if (trimIndex < 0) {
//            return origLength - 1;
//        } else {
//            return trimIndex - 1;
//        }
//    }
//
//    /**
//     * Extracts the post-dial sequence of DTMF control digits, pauses, and
//     * waits. Strips separators. This string may be empty, but will not be null
//     * unless phoneNumber == null.
//     *
//     * Returns null if phoneNumber == null
//     */
//
//    public static String
//    extractPostDialPortion(String phoneNumber) {
//        if (phoneNumber == null) return null;
//
//        int trimIndex;
//        StringBuilder ret = new StringBuilder();
//
//        trimIndex = indexOfLastNetworkChar (phoneNumber);
//
//        for (int i = trimIndex + 1, s = phoneNumber.length()
//                ; i < s; i++
//        ) {
//            char c = phoneNumber.charAt(i);
//            if (isNonSeparator(c)) {
//                ret.append(c);
//            }
//        }
//
//        return ret.toString();
//    }
//
//    /**
//     * Compare phone numbers a and b, return true if they're identical
//     * enough for caller ID purposes.
//     *
//     * - Compares from right to left
//     * - requires MIN_MATCH (5) characters to match
//     * - handles common trunk prefixes and international prefixes
//     *   (basically, everything except the Russian trunk prefix)
//     *
//     * Tolerates nulls
//     */
//    public static boolean
//    compare(String a, String b) {
//        int ia, ib;
//        int matched;
//
//        if (a == null || b == null) return a == b;
//
//        if (a.length() == 0 || b.length() == 0) {
//            return false;
//        }
//
//        ia = indexOfLastNetworkChar (a);
//        ib = indexOfLastNetworkChar (b);
//        matched = 0;
//
//        while (ia >= 0 && ib >=0) {
//            char ca, cb;
//            boolean skipCmp = false;
//
//            ca = a.charAt(ia);
//
//            if (!isDialable(ca)) {
//                ia--;
//                skipCmp = true;
//            }
//
//            cb = b.charAt(ib);
//
//            if (!isDialable(cb)) {
//                ib--;
//                skipCmp = true;
//            }
//
//            if (!skipCmp) {
//                if (cb != ca && ca != WILD && cb != WILD) {
//                    break;
//                }
//                ia--; ib--; matched++;
//            }
//        }
//
//        if (matched < MIN_MATCH) {
//            int aLen = a.length();
//
//            // if the input strings match, but their lengths < MIN_MATCH,
//            // treat them as equal.
//            if (aLen == b.length() && aLen == matched) {
//                return true;
//            }
//            return false;
//        }
//
//        // At least one string has matched completely;
//        if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
//            return true;
//        }
//
//        /*
//         * Now, what remains must be one of the following for a
//         * match:
//         *
//         *  - a '+' on one and a '00' or a '011' on the other
//         *  - a '0' on one and a (+,00)<country code> on the other
//         *     (for this, a '0' and a '00' prefix would have succeeded above)
//         */
//
//        if (matchIntlPrefix(a, ia + 1)
//            && matchIntlPrefix (b, ib +1)
//        ) {
//            return true;
//        }
//
//        if (matchTrunkPrefix(a, ia + 1)
//            && matchIntlPrefixAndCC(b, ib +1)
//        ) {
//            return true;
//        }
//
//        if (matchTrunkPrefix(b, ib + 1)
//            && matchIntlPrefixAndCC(a, ia +1)
//        ) {
//            return true;
//        }
//
//        return false;
//    }
//
//    /**
//     * Returns the rightmost MIN_MATCH (5) characters in the network portion
//     * in *reversed* order
//     *
//     * This can be used to do a database lookup against the column
//     * that stores getStrippedReversed()
//     *
//     * Returns null if phoneNumber == null
//     */
//    public static String
//    toCallerIDMinMatch(String phoneNumber) {
//        String np = extractNetworkPortion(phoneNumber);
//        return internalGetStrippedReversed(np, MIN_MATCH);
//    }
//
//    /**
//     * Returns the network portion reversed.
//     * This string is intended to go into an index column for a
//     * database lookup.
//     *
//     * Returns null if phoneNumber == null
//     */
//    public static String
//    getStrippedReversed(String phoneNumber) {
//        String np = extractNetworkPortion(phoneNumber);
//
//        if (np == null) return null;
//
//        return internalGetStrippedReversed(np, np.length());
//    }
//
//    /**
//     * Returns the last numDigits of the reversed phone number
//     * Returns null if np == null
//     */
//    private static String
//    internalGetStrippedReversed(String np, int numDigits) {
//        if (np == null) return null;
//
//        StringBuilder ret = new StringBuilder(numDigits);
//        int length = np.length();
//
//        for (int i = length - 1, s = length
//            ; i >= 0 && (s - i) <= numDigits ; i--
//        ) {
//            char c = np.charAt(i);
//
//            ret.append(c);
//        }
//
//        return ret.toString();
//    }
//
//    /**
//     * Basically: makes sure there's a + in front of a
//     * TOA_International number
//     *
//     * Returns null if s == null
//     */
//    public static String
//    stringFromStringAndTOA(String s, int TOA) {
//        if (s == null) return null;
//
//        if (TOA == TOA_International && s.length() > 0 && s.charAt(0) != '+') {
//            return "+" + s;
//        }
//
//        return s;
//    }
//
//    /**
//     * Returns the TOA for the given dial string
//     * Basically, returns TOA_International if there's a + prefix
//     */
//
//    public static int
//    toaFromString(String s) {
//        if (s != null && s.length() > 0 && s.charAt(0) == '+') {
//            return TOA_International;
//        }
//
//        return TOA_Unknown;
//    }
//
//    /**
//     * Phone numbers are stored in "lookup" form in the database
//     * as reversed strings to allow for caller ID lookup
//     *
//     * This method takes a phone number and makes a valid SQL "LIKE"
//     * string that will match the lookup form
//     *
//     */
//    /** all of a up to len must be an international prefix or
//     *  separators/non-dialing digits
//     */
//    private static boolean
//    matchIntlPrefix(String a, int len) {
//        /* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
//        /*        0       1                           2 3 45               */
//
//        int state = 0;
//        for (int i = 0 ; i < len ; i++) {
//            char c = a.charAt(i);
//
//            switch (state) {
//                case 0:
//                    if      (c == '+') state = 1;
//                    else if (c == '0') state = 2;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                case 2:
//                    if      (c == '0') state = 3;
//                    else if (c == '1') state = 4;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                case 4:
//                    if      (c == '1') state = 5;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                default:
//                    if (isNonSeparator(c)) return false;
//                break;
//
//            }
//        }
//
//        return state == 1 || state == 3 || state == 5;
//    }
//
//    /**
//     *  3GPP TS 24.008 10.5.4.7
//     *  Called Party BCD Number
//     *
//     *  See Also TS 51.011 10.5.1 "dialing number/ssc string"
//     *  and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
//     *
//     * @param bytes the data buffer
//     * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
//     * @param length is the number of bytes including TOA byte
//     *                and must be at least 2
//     *
//     * @return partial string on invalid decode
//     *
//     * FIXME(mkf) support alphanumeric address type
//     *  currently implemented in SMSMessage.getAddress()
//     */
//    public static String
//    calledPartyBCDToString (byte[] bytes, int offset, int length) {
//        boolean prependPlus = false;
//        StringBuilder ret = new StringBuilder(1 + length * 2);
//
//        if (length < 2) {
//            return "";
//        }
//
//        if ((bytes[offset] & 0xff) == TOA_International) {
//            prependPlus = true;
//        }
//
//        internalCalledPartyBCDFragmentToString(
//                ret, bytes, offset + 1, length - 1);
//
//        if (prependPlus && ret.length() == 0) {
//            // If the only thing there is a prepended plus, return ""
//            return "";
//        }
//
//        if (prependPlus) {
//            // This is an "international number" and should have
//            // a plus prepended to the dialing number. But there
//            // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
//            // so we need to handle those also.
//            //
//            // http://web.telia.com/~u47904776/gsmkode.htm is a
//            // has a nice list of some of these GSM codes.
//            //
//            // Examples are:
//            //   **21*+886988171479#
//            //   **21*8311234567#
//            //   *21#
//            //   #21#
//            //   *#21#
//            //   *31#+11234567890
//            //   #31#+18311234567
//            //   #31#8311234567
//            //   18311234567
//            //   +18311234567#
//            //   +18311234567
//            // Odd ball cases that some phones handled
//            // where there is no dialing number so they
//            // append the "+"
//            //   *21#+
//            //   **21#+
//            String retString = ret.toString();
//            Pattern p = Pattern.compile("(^[#*])(.*)([#*])(.*)(#)$");
//            Matcher m = p.matcher(retString);
//            if (m.matches()) {
//                if ("".equals(m.group(2))) {
//                    // Started with two [#*] ends with #
//                    // So no dialing number and we'll just
//                    // append a +, this handles **21#+
//                    ret = new StringBuilder();
//                    ret.append(m.group(1));
//                    ret.append(m.group(3));
//                    ret.append(m.group(4));
//                    ret.append(m.group(5));
//                    ret.append("+");
//                } else {
//                    // Starts with [#*] and ends with #
//                    // Assume group 4 is a dialing number
//                    // such as *21*+1234554#
//                    ret = new StringBuilder();
//                    ret.append(m.group(1));
//                    ret.append(m.group(2));
//                    ret.append(m.group(3));
//                    ret.append("+");
//                    ret.append(m.group(4));
//                    ret.append(m.group(5));
//                }
//            } else {
//                p = Pattern.compile("(^[#*])(.*)([#*])(.*)");
//                m = p.matcher(retString);
//                if (m.matches()) {
//                    // Starts with [#*] and only one other [#*]
//                    // Assume the data after last [#*] is dialing
//                    // number (i.e. group 4) such as *31#+11234567890.
//                    // This also includes the odd ball *21#+
//                    ret = new StringBuilder();
//                    ret.append(m.group(1));
//                    ret.append(m.group(2));
//                    ret.append(m.group(3));
//                    ret.append("+");
//                    ret.append(m.group(4));
//                } else {
//                    // Does NOT start with [#*] just prepend '+'
//                    ret = new StringBuilder();
//                    ret.append('+');
//                    ret.append(retString);
//                }
//            }
//        }
//
//        return ret.toString();
//    }
//
//    private static void
//    internalCalledPartyBCDFragmentToString(
//        StringBuilder sb, byte [] bytes, int offset, int length) {
//        for (int i = offset ; i < length + offset ; i++) {
//            byte b;
//            char c;
//
//            c = bcdToChar((byte)(bytes[i] & 0xf));
//
//            if (c == 0) {
//                return;
//            }
//            sb.append(c);
//
//            // FIXME(mkf) TS 23.040 9.1.2.3 says
//            // "if a mobile receives 1111 in a position prior to
//            // the last semi-octet then processing shall commense with
//            // the next semi-octet and the intervening
//            // semi-octet shall be ignored"
//            // How does this jive with 24,008 10.5.4.7
//
//            b = (byte)((bytes[i] >> 4) & 0xf);
//
//            if (b == 0xf && i + 1 == length + offset) {
//                //ignore final 0xf
//                break;
//            }
//
//            c = bcdToChar(b);
//            if (c == 0) {
//                return;
//            }
//
//            sb.append(c);
//        }
//
//    }
//
//    /**
//     * Like calledPartyBCDToString, but field does not start with a
//     * TOA byte. For example: SIM ADN extension fields
//     */
//
//    public static String
//    calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
//        StringBuilder ret = new StringBuilder(length * 2);
//
//        internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
//
//        return ret.toString();
//    }
//
//    /** returns 0 on invalid value */
//    private static char
//    bcdToChar(byte b) {
//        if (b < 0xa) {
//            return (char)('0' + b);
//        } else switch (b) {
//            case 0xa: return '*';
//            case 0xb: return '#';
//            case 0xc: return PAUSE;
//            case 0xd: return WILD;
//
//            default: return 0;
//        }
//    }
//
//    private static int
//    charToBCD(char c) {
//        if (c >= '0' && c <= '9') {
//            return c - '0';
//        } else if (c == '*') {
//            return 0xa;
//        } else if (c == '#') {
//            return 0xb;
//        } else if (c == PAUSE) {
//            return 0xc;
//        } else if (c == WILD) {
//            return 0xd;
//        } else {
//            throw new RuntimeException ("invalid char for BCD " + c);
//        }
//    }
//
//    /**
//     * Return true iff the network portion of <code>address</code> is,
//     * as far as we can tell on the device, suitable for use as an SMS
//     * destination address.
//     */
//    public static boolean isWellFormedSmsAddress(String address) {
//        String networkPortion =
//                PhoneNumberUtils.extractNetworkPortion(address);
//
//        return (!(networkPortion.equals("+")
//                  || TextUtils.isEmpty(networkPortion)))
//               && isDialable(networkPortion);
//    }
//
//    public static boolean isGlobalPhoneNumber(String phoneNumber) {
//        if (TextUtils.isEmpty(phoneNumber)) {
//            return false;
//        }
//
//        Matcher match = GLOBAL_PHONE_NUMBER_PATTERN.matcher(phoneNumber);
//        return match.matches();
//    }
//
//    private static boolean isDialable(String address) {
//        for (int i = 0, count = address.length(); i < count; i++) {
//            if (!isDialable(address.charAt(i))) {
//                return false;
//            }
//        }
//        return true;
//    }
//
//    /**
//     * Note: calls extractNetworkPortion(), so do not use for
//     * SIM EF[ADN] style records
//     *
//     * Returns null if network portion is empty.
//     */
//    public static byte[]
//    networkPortionToCalledPartyBCD(String s) {
//        String networkPortion = extractNetworkPortion(s);
//        return numberToCalledPartyBCDHelper(networkPortion, false);
//    }
//
//    /**
//     * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
//     * one-byte length prefix.
//     */
//    public static byte[]
//    networkPortionToCalledPartyBCDWithLength(String s) {
//        String networkPortion = extractNetworkPortion(s);
//        return numberToCalledPartyBCDHelper(networkPortion, true);
//    }
//
//    /**
//     * Convert a dialing number to BCD byte array
//     *
//     * @param number dialing number string
//     *        if the dialing number starts with '+', set to internationl TOA
//     * @return BCD byte array
//     */
//    public static byte[]
//    numberToCalledPartyBCD(String number) {
//        return numberToCalledPartyBCDHelper(number, false);
//    }
//
//    /**
//     * If includeLength is true, prepend a one-byte length value to
//     * the return array.
//     */
//    private static byte[]
//    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
//        int numberLenReal = number.length();
//        int numberLenEffective = numberLenReal;
//        boolean hasPlus = number.indexOf('+') != -1;
//        if (hasPlus) numberLenEffective--;
//
//        if (numberLenEffective == 0) return null;
//
//        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
//        int extraBytes = 1;                            // Prepended TOA byte.
//        if (includeLength) extraBytes++;               // Optional prepended length byte.
//        resultLen += extraBytes;
//
//        byte[] result = new byte[resultLen];
//
//        int digitCount = 0;
//        for (int i = 0; i < numberLenReal; i++) {
//            char c = number.charAt(i);
//            if (c == '+') continue;
//            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
//            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
//            digitCount++;
//        }
//
//        // 1-fill any trailing odd nibble/quartet.
//        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
//
//        int offset = 0;
//        if (includeLength) result[offset++] = (byte)(resultLen - 1);
//        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
//
//        return result;
//    }
//
//    /** all of 'a' up to len must match non-US trunk prefix ('0') */
//    private static boolean
//    matchTrunkPrefix(String a, int len) {
//        boolean found;
//
//        found = false;
//
//        for (int i = 0 ; i < len ; i++) {
//            char c = a.charAt(i);
//
//            if (c == '0' && !found) {
//                found = true;
//            } else if (isNonSeparator(c)) {
//                return false;
//            }
//        }
//
//        return found;
//    }
//
//    /** all of 'a' up to len must be a (+|00|011)country code)
//     *  We're fast and loose with the country code. Any \d{1,3} matches */
//    private static boolean
//    matchIntlPrefixAndCC(String a, int len) {
//        /*  [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
//        /*      0          1 2 3 45  6 7  8                 */
//
//        int state = 0;
//        for (int i = 0 ; i < len ; i++ ) {
//            char c = a.charAt(i);
//
//            switch (state) {
//                case 0:
//                    if      (c == '+') state = 1;
//                    else if (c == '0') state = 2;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                case 2:
//                    if      (c == '0') state = 3;
//                    else if (c == '1') state = 4;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                case 4:
//                    if      (c == '1') state = 5;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                case 1:
//                case 3:
//                case 5:
//                    if      (isISODigit(c)) state = 6;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                case 6:
//                case 7:
//                    if      (isISODigit(c)) state++;
//                    else if (isNonSeparator(c)) return false;
//                break;
//
//                default:
//                    if (isNonSeparator(c)) return false;
//            }
//        }
//
//        return state == 6 || state == 7 || state == 8;
//    }
//
//    //================ Number formatting =========================
//
    /** The current locale is unknown, look for a country code or don't format */
    public static final int FORMAT_UNKNOWN = 0;
    /** NANP formatting */
    public static final int FORMAT_NANP = 1;
    /** Japastaticnese formatting */
    public static final int FORMAT_JAPAN = 2;

    /** List of country codes for countries that use the NANP */
    private static final String[] NANP_COUNTRIES = new String[] {
        "US", // United States
        "CA", // Canada
        "AS", // American Samoa
        "AI", // Anguilla
        "AG", // Antigua and Barbuda
        "BS", // Bahamas
        "BB", // Barbados
        "BM", // Bermuda
        "VG", // British Virgin Islands
        "KY", // Cayman Islands
        "DM", // Dominica
        "DO", // Dominican Republic
        "GD", // Grenada
        "GU", // Guam
        "JM", // Jamaica
        "PR", // Puerto Rico
        "MS", // Montserrat
        "NP", // Northern Mariana Islands
        "KN", // Saint Kitts and Nevis
        "LC", // Saint Lucia
        "VC", // Saint Vincent and the Grenadines
        "TT", // Trinidad and Tobago
        "TC", // Turks and Caicos Islands
        "VI", // U.S. Virgin Islands
    };

    /**
     * Breaks the given number down and formats it according to the rules
     * for the country the number is from.
     *
     * @param source the phone number to format
     * @return a locally acceptable formatting of the input, or the raw input if
     *  formatting rules aren't known for the number
     */
    public static String formatNumber(String source) {
        SpannableStringBuilder text = new SpannableStringBuilder(source);
        formatNumber(text, getFormatTypeForLocale(Locale.getDefault()));
        return text.toString();
    }

    /**
     * Returns the phone number formatting type for the given locale.
     *
     * @param locale The locale of interest, usually {@link Locale#getDefault()}
     * @return the formatting type for the given locale, or FORMAT_UNKNOWN if the formatting
     * rules are not known for the given locale
     */
    public static int getFormatTypeForLocale(Locale locale) {
        String country = locale.getCountry();

        // Check for the NANP countries
        int length = NANP_COUNTRIES.length;
        for (int i = 0; i < length; i++) {
            if (NANP_COUNTRIES[i].equals(country)) {
                return FORMAT_NANP;
            }
        }
        if (locale.equals(Locale.JAPAN)) {
            return FORMAT_JAPAN;
        }
        return FORMAT_UNKNOWN;
    }

    /**
     * Formats a phone number in-place. Currently only supports NANP formatting.
     *
     * @param text The number to be formatted, will be modified with the formatting
     * @param defaultFormattingType The default formatting rules to apply if the number does
     * not begin with +<country_code>
     */
    public static void formatNumber(Editable text, int defaultFormattingType) {
        int formatType = defaultFormattingType;

        if (text.length() > 2 && text.charAt(0) == '+') {
            if (text.charAt(1) == '1') {
                formatType = FORMAT_NANP;
            } else if (text.length() >= 3 && text.charAt(1) == '8'
                && text.charAt(2) == '1') {
                formatType = FORMAT_JAPAN;
            } else {
                return;
            }
        }

        switch (formatType) {
            case FORMAT_NANP:
                formatNanpNumber(text);
                return;
            case FORMAT_JAPAN:
                formatJapaneseNumber(text);
                return;
        }
    }

    private static final int NANP_STATE_DIGIT = 1;
    private static final int NANP_STATE_PLUS = 2;
    private static final int NANP_STATE_ONE = 3;
    private static final int NANP_STATE_DASH = 4;

    /**
     * Formats a phone number in-place using the NANP formatting rules. Numbers will be formatted
     * as:
     *
     * <p><code>
     * xxx-xxxx
     * xxx-xxx-xxxx
     * 1-xxx-xxx-xxxx
     * +1-xxx-xxx-xxxx
     * </code></p>
     *
     * @param text the number to be formatted, will be modified with the formatting
     */
    public static void formatNanpNumber(Editable text) {
        int length = text.length();
        if (length > "+1-nnn-nnn-nnnn".length()) {
            // The string is too long to be formatted
            return;
        }
        CharSequence saved = text.subSequence(0, length);

        // Strip the dashes first, as we're going to add them back
        int p = 0;
        while (p < text.length()) {
            if (text.charAt(p) == '-') {
                text.delete(p, p + 1);
            } else {
                p++;
            }
        }
        length = text.length();

        // When scanning the number we record where dashes need to be added,
        // if they're non-0 at the end of the scan the dashes will be added in
        // the proper places.
        int dashPositions[] = new int[3];
        int numDashes = 0;

        int state = NANP_STATE_DIGIT;
        int numDigits = 0;
        for (int i = 0; i < length; i++) {
            char c = text.charAt(i);
            switch (c) {
                case '1':
                    if (numDigits == 0 || state == NANP_STATE_PLUS) {
                        state = NANP_STATE_ONE;
                        break;
                    }
                    // fall through
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '0':
                    if (state == NANP_STATE_PLUS) {
                        // Only NANP number supported for now
                        text.replace(0, length, saved);
                        return;
                    } else if (state == NANP_STATE_ONE) {
                        // Found either +1 or 1, follow it up with a dash
                        dashPositions[numDashes++] = i;
                    } else if (state != NANP_STATE_DASH && (numDigits == 3 || numDigits == 6)) {
                        // Found a digit that should be after a dash that isn't
                        dashPositions[numDashes++] = i;
                    }
                    state = NANP_STATE_DIGIT;
                    numDigits++;
                    break;

                case '-':
                    state = NANP_STATE_DASH;
                    break;

                case '+':
                    if (i == 0) {
                        // Plus is only allowed as the first character
                        state = NANP_STATE_PLUS;
                        break;
                    }
                    // Fall through
                default:
                    // Unknown character, bail on formatting
                    text.replace(0, length, saved);
                    return;
            }
        }

        if (numDigits == 7) {
            // With 7 digits we want xxx-xxxx, not xxx-xxx-x
            numDashes--;
        }

        // Actually put the dashes in place
        for (int i = 0; i < numDashes; i++) {
            int pos = dashPositions[i];
            text.replace(pos + i, pos + i, "-");
        }

        // Remove trailing dashes
        int len = text.length();
        while (len > 0) {
            if (text.charAt(len - 1) == '-') {
                text.delete(len - 1, len);
                len--;
            } else {
                break;
            }
        }
    }

    /**
     * Formats a phone number in-place using the Japanese formatting rules.
     * Numbers will be formatted as:
     *
     * <p><code>
     * 03-xxxx-xxxx
     * 090-xxxx-xxxx
     * 0120-xxx-xxx
     * +81-3-xxxx-xxxx
     * +81-90-xxxx-xxxx
     * </code></p>
     *
     * @param text the number to be formatted, will be modified with
     * the formatting
     */
    public static void formatJapaneseNumber(Editable text) {
        JapanesePhoneNumberFormatter.format(text);
    }
//
//    // Three and four digit phone numbers for either special services
//    // or from the network (eg carrier-originated SMS messages) should
//    // not match
//    static final int MIN_MATCH = 5;
//
//    /**
//     * isEmergencyNumber: checks a given number against the list of
//     *   emergency numbers provided by the RIL and SIM card.
//     *
//     * @param number the number to look up.
//     * @return if the number is in the list of emergency numbers
//     * listed in the ril / sim, then return true, otherwise false.
//     */
//    public static boolean isEmergencyNumber(String number) {
//        // Strip the separators from the number before comparing it
//        // to the list.
//        number = extractNetworkPortion(number);
//
//        // retrieve the list of emergency numbers
//        String numbers = SystemProperties.get("ro.ril.ecclist");
//
//        if (!TextUtils.isEmpty(numbers)) {
//            // searches through the comma-separated list for a match,
//            // return true if one is found.
//            for (String emergencyNum : numbers.split(",")) {
//                if (emergencyNum.equals(number)) {
//                    return true;
//                }
//            }
//            // no matches found against the list!
//            return false;
//        }
//
//        //no ecclist system property, so use our own list.
//        return (number.equals("112") || number.equals("911"));
//    }
//
//    /**
//     * Translates any alphabetic letters (i.e. [A-Za-z]) in the
//     * specified phone number into the equivalent numeric digits,
//     * according to the phone keypad letter mapping described in
//     * ITU E.161 and ISO/IEC 9995-8.
//     *
//     * @return the input string, with alpha letters converted to numeric
//     *         digits using the phone keypad letter mapping.  For example,
//     *         an input of "1-800-GOOG-411" will return "1-800-4664-411".
//     */
//    public static String convertKeypadLettersToDigits(String input) {
//        if (input == null) {
//            return input;
//        }
//        int len = input.length();
//        if (len == 0) {
//            return input;
//        }
//
//        char[] out = input.toCharArray();
//
//        for (int i = 0; i < len; i++) {
//            char c = out[i];
//            // If this char isn't in KEYPAD_MAP at all, just leave it alone.
//            out[i] = (char) KEYPAD_MAP.get(c, c);
//        }
//
//        return new String(out);
//    }
//
//    /**
//     * The phone keypad letter mapping (see ITU E.161 or ISO/IEC 9995-8.)
//     * TODO: This should come from a resource.
//     */
//    private static final SparseIntArray KEYPAD_MAP = new SparseIntArray();
//    static {
//        KEYPAD_MAP.put('a', '2'); KEYPAD_MAP.put('b', '2'); KEYPAD_MAP.put('c', '2');
//        KEYPAD_MAP.put('A', '2'); KEYPAD_MAP.put('B', '2'); KEYPAD_MAP.put('C', '2');
//
//        KEYPAD_MAP.put('d', '3'); KEYPAD_MAP.put('e', '3'); KEYPAD_MAP.put('f', '3');
//        KEYPAD_MAP.put('D', '3'); KEYPAD_MAP.put('E', '3'); KEYPAD_MAP.put('F', '3');
//
//        KEYPAD_MAP.put('g', '4'); KEYPAD_MAP.put('h', '4'); KEYPAD_MAP.put('i', '4');
//        KEYPAD_MAP.put('G', '4'); KEYPAD_MAP.put('H', '4'); KEYPAD_MAP.put('I', '4');
//
//        KEYPAD_MAP.put('j', '5'); KEYPAD_MAP.put('k', '5'); KEYPAD_MAP.put('l', '5');
//        KEYPAD_MAP.put('J', '5'); KEYPAD_MAP.put('K', '5'); KEYPAD_MAP.put('L', '5');
//
//        KEYPAD_MAP.put('m', '6'); KEYPAD_MAP.put('n', '6'); KEYPAD_MAP.put('o', '6');
//        KEYPAD_MAP.put('M', '6'); KEYPAD_MAP.put('N', '6'); KEYPAD_MAP.put('O', '6');
//
//        KEYPAD_MAP.put('p', '7'); KEYPAD_MAP.put('q', '7'); KEYPAD_MAP.put('r', '7'); KEYPAD_MAP.put('s', '7');
//        KEYPAD_MAP.put('P', '7'); KEYPAD_MAP.put('Q', '7'); KEYPAD_MAP.put('R', '7'); KEYPAD_MAP.put('S', '7');
//
//        KEYPAD_MAP.put('t', '8'); KEYPAD_MAP.put('u', '8'); KEYPAD_MAP.put('v', '8');
//        KEYPAD_MAP.put('T', '8'); KEYPAD_MAP.put('U', '8'); KEYPAD_MAP.put('V', '8');
//
//        KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
//        KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
//    }
}
